package studia.figlewicz.math.library;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class GeneratorLiczbLosowych {

	private boolean czyGenerowany = false;
	
	// konstruktory
	public GeneratorLiczbLosowych() {
		generator = new Random();
	}
	public GeneratorLiczbLosowych(Long seed) {
		this.seed = seed;
		try {
			generator = new Random(seed);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
	public GeneratorLiczbLosowych(ArrayList<ZmiennaLosowa> zmienneLosowe) {
		generator = new Random();
		for (ZmiennaLosowa zl : zmienneLosowe) {
			this.zmienneLosowe.put(zl, null);
		}
		this.inicjalizujMacierzKorelacji();
	}
	public GeneratorLiczbLosowych(ZmiennaLosowa zmiennaLosowa) {
		generator = new Random();
		this.zmienneLosowe.put(zmiennaLosowa, null);
		this.inicjalizujMacierzKorelacji();
	}
	public GeneratorLiczbLosowych(Long seed, ArrayList<ZmiennaLosowa> zmienneLosowe) {
		this.seed = seed;
		generator = new Random();
		for (ZmiennaLosowa zl : zmienneLosowe) {
			this.zmienneLosowe.put(zl, null);
		}
		this.inicjalizujMacierzKorelacji();
	}
	public GeneratorLiczbLosowych(Long seed, ZmiennaLosowa zmiennaLosowa) {
		this.seed = seed;
		generator = new Random();
		this.zmienneLosowe.put(zmiennaLosowa, null);
		this.inicjalizujMacierzKorelacji();
	}
	
	
	private Map<ZmiennaLosowa,Double> zmienneLosowe = new HashMap<ZmiennaLosowa,Double>();
	public Map<ZmiennaLosowa,Double> getZmienneLosowe() {
		return zmienneLosowe;
	}
	public void setZmienneLosowe(Map<ZmiennaLosowa,Double> zmienneLosowe) {
		this.zmienneLosowe = zmienneLosowe;
	}
	public void addZmiennaLosowa(ZmiennaLosowa zmiennaLosowa) {
		zmienneLosowe.put(zmiennaLosowa, null);
		zmiennaLosowa.setGenerator(this);
		czyGenerowany = false;
	}
	private void resetujZmienneLosowe() {
		for (ZmiennaLosowa zl : zmienneLosowe.keySet()) {
			zmienneLosowe.put(zl, null);
		}
		czyGenerowany = false;
	}
	public Double getWartoscZmiennejLosowej(ZmiennaLosowa zmiennaLosowa) {
		Double resoult = null;
		if (!czyGenerowany)
			run();
		resoult = new Double(zmienneLosowe.get(zmiennaLosowa).doubleValue());
		return resoult;
	}
	
	
	private Long seed;
	public long getSeed() {
		return seed.longValue();
	}
	public void setSeed(Long seed) {
		this.seed = seed;
		this.generator.setSeed(seed);
	}
	
	
	private Random generator;
	public Random getGenerator() {
		return generator;
	}
	
	
	private Map<ZmiennaLosowa,Map<ZmiennaLosowa,Double>> macierzKorelacji = null;
	public Map<ZmiennaLosowa,Map<ZmiennaLosowa,Double>> getMacierzKorelacji() {
		return macierzKorelacji;
	}
	public void setMacierzKorelacji(Map<ZmiennaLosowa,Map<ZmiennaLosowa,Double>> macierzKorelacji) {
		this.macierzKorelacji = macierzKorelacji;
	}
	private void inicjalizujMacierzKorelacji() {
		macierzKorelacji = new HashMap<ZmiennaLosowa,Map<ZmiennaLosowa,Double>>();
		
		for (ZmiennaLosowa zl : zmienneLosowe.keySet()) {
			Map<ZmiennaLosowa,Double> wektor = new HashMap<ZmiennaLosowa,Double>();
			
			for (ZmiennaLosowa skorelowana : zmienneLosowe.keySet()) {

				// 1 na diagonali, o poza diagonala
				if (zl == skorelowana)
					wektor.put(skorelowana, new Double(1));
				else
					wektor.put(skorelowana, new Double(0));
				
			}
			macierzKorelacji.put(zl, wektor);
		}
	}
	private Double getKorelacja(ZmiennaLosowa zm1, ZmiennaLosowa zm2) {
		Double corr = macierzKorelacji.get(zm1).get(zm2);
		return corr;
	}
	private void setKorelacja(ZmiennaLosowa zm1, ZmiennaLosowa zm2, Double korelacja) {
		Double corr = macierzKorelacji.get(zm1).get(zm2);
		corr = korelacja;
	}
	private void setKorelacje(ZmiennaLosowa zm1, ZmiennaLosowa zm2, Double korelacja) {
		setKorelacja(zm1,zm2,korelacja);
		setKorelacja(zm2,zm1,korelacja);
	}
	public void resetujMacierzKorelacji() {
		if (macierzKorelacji == null)
			return;
		inicjalizujMacierzKorelacji();
	}

	public boolean czyGenerowany() {
		return czyGenerowany;
	}
	public void reset(long seed) {
		this.seed = new Long(seed);
		try {
			generator = new Random(seed);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
	public Double getRandomGaussian(double mu, double sigma) {
		Double variable = null;
		try {
			variable = Double.valueOf(generator.nextGaussian());
			variable = variable * sigma + mu;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return variable;
	}
	
	public Double getRandomDouble(double a, double b) {
		Double variable = null;
		try {
			variable = Double.valueOf(generator.nextDouble());
			variable = variable * (b - a) + a;
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return variable;
	}
	private ArrayList<Double> getRandomGaussianList(int rozmiar, double mu, double sigma) {
		ArrayList<Double> wektor = new ArrayList<Double>();
		
		Double variable;
		try {
			for (int i=0; i<rozmiar; i++) {
				variable = new Double(generator.nextGaussian());
				variable = variable * sigma + mu;
				wektor.add(variable);
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return wektor;
	}
	private ArrayList<Double> getRandomDoubleList(int rozmiar, double a, double b) {
		ArrayList<Double> wektor = new ArrayList<Double>();
		
		Double variable;
		try {
			for (int i=0; i<rozmiar; i++) {
				variable = new Double(generator.nextDouble());
				variable = variable * (b - a) + a;
				wektor.add(variable);
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return wektor;
	}
	private Double getRandomByDensity(RozkladZmiennejLosowej rozklad){
		Double resoult;
		if (rozklad == RozkladZmiennejLosowej.JEDNOSTAJNY)
			resoult =  new Double(getRandomDouble(0,1).doubleValue());
		else if (rozklad == RozkladZmiennejLosowej.NORMALNY)
			resoult =  new Double(getRandomGaussian(0,1).doubleValue());
		else
			resoult = null;
		return resoult;
	}
	
	public void run() {
		resetujZmienneLosowe();
		for (ZmiennaLosowa zl : zmienneLosowe.keySet()) {
			Double wartosc = null;
			
			if (zl.getRozklad() == RozkladZmiennejLosowej.JEDNOSTAJNY)
				wartosc = getRandomDouble(zl.getParametr1(), zl.getParametr2());
			else if (zl.getRozklad() == RozkladZmiennejLosowej.NORMALNY)
				wartosc = getRandomGaussian(zl.getParametr1(), zl.getParametr2());
			
			zmienneLosowe.put(zl,wartosc);
		}
		czyGenerowany = true;
	}
	
	
}


